package com.lagou.factory;

import com.lagou.anno.*;
import org.springframework.beans.factory.annotation.Qualifier;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;

/**
 * 用于解析注解并管理管理对象的创建和依赖关系，事务的管理
 */
public class BeanFactory {


    private static final Map<String, Object> map = new HashMap<>();  // 存储对象

    // 有依赖的对象set
    private static final Set<Object> autoWiredClassSet = new HashSet<>();

    private static final Set<Object> transactionClassesSet = new HashSet<>();

    private static final String PROXY_CLASS_NAME = "proxyFactory";

    private static final String JAVA_SUFFIX = ".java";

    private static final String FILE_ABSOLUTE_SEPARATOR = Matcher.quoteReplacement(File.separator);

    private static final String FILE_PROJECT_SEPARATOR = "\\.";

    private static final String STRING_FILE_SEPARATOR = ".";

    static {
        // project path
        String packPath = System.getProperty("user.dir");
        /*String packPath = "D:\\IdeaProjects\\lagou-transfer-anno";*/
        String scanPath = "com.lagou";
        File file = new File(packPath);
        try {
            initClasses(file, scanPath.replaceAll(FILE_PROJECT_SEPARATOR, FILE_ABSOLUTE_SEPARATOR));
            autowireClasses();
            setTransactionProxyClasses();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置事务代理类
     */
    private static void setTransactionProxyClasses() {
        ProxyFactory proxyFactory = (ProxyFactory) map.get(PROXY_CLASS_NAME);
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (entry.getKey().equals(PROXY_CLASS_NAME)
                    || transactionClassesSet.contains(entry)) {
                continue;
            }
            Object target = entry.getValue();
            Class<?> aClass = target.getClass();
            Method[] methods = aClass.getMethods();
            for (Method method : methods) {
                if (method.getAnnotation(Transactional.class) != null) {
                    Class<?>[] interfaces = aClass.getInterfaces();
                    Object proxy = null;
                    if (interfaces != null && interfaces.length > 0) {
                        proxy = proxyFactory.getProxy(target);
                    } else {
                        proxy = proxyFactory.getCglibProxy(target);
                    }
                    map.put(entry.getKey(), proxy);
                    transactionClassesSet.add(target);
                    break;
                }
            }
        }
    }

    /**
     * 自动注入
     */
    private static void autowireClasses() throws IllegalAccessException, ClassNotFoundException {
        for (Object wiredObject : autoWiredClassSet) {
            Class<?> aClass = wiredObject.getClass();
            Field[] fields = aClass.getDeclaredFields();
            for (Field field : fields) {
                Autowired autowired = field.getAnnotation(Autowired.class);
                if (autowired == null) {
                    continue;
                }
                String autowireClassName = null;
                // 是否指定了对应的实现类名
                // 若已经指定了对象的名字，则直接使用指定的名字找对象
                Qualifier qualifier = field.getAnnotation(Qualifier.class);
                if (qualifier != null && qualifier.value() != null && !qualifier.value().equals("")) {
                    autowireClassName = qualifier.value();
                } else {
                    // 若未指定对象名称，则使用声明的类型的驼峰式名称寻找注入的目标对象
                    autowireClassName = getCamelClassName(field.getType().getName());
                }
                field.setAccessible(true);
                // 若目标对象不存在，则抛出异常
                if (map.get(autowireClassName) == null) {
                    throw new ClassNotFoundException("注入对象不存在");
                }
                field.set(wiredObject, map.get(autowireClassName));
            }
        }
    }

    /**
     * 循环遍历目录，初始化目录下的Java文件
     * @param file
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    private static void initClasses(File file, String scanPath) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            // 循环遍历目录
            if (files != null) {
                for (File f : files) {
                    initClasses(f, scanPath);
                }
            }
        } else {
            if (file.getName().endsWith(JAVA_SUFFIX)) {
                String path = file.getPath();
                int scanPathIndex = path.indexOf(scanPath);
                int javaSuffixIndex = path.indexOf(JAVA_SUFFIX);
                Class<?> aClass = Class.forName(path.substring(scanPathIndex, javaSuffixIndex).replaceAll(FILE_ABSOLUTE_SEPARATOR, FILE_PROJECT_SEPARATOR));
                if (aClass.isInterface()) {
                    return;
                }
                Service service = aClass.getAnnotation(Service.class);
                if (service != null) {
                    doInitClass(aClass, service.value());
                    return;
                }
                Repository repository = aClass.getAnnotation(Repository.class);
                if (repository != null) {
                    doInitClass(aClass, repository.value());
                    return;
                }
                Component component = aClass.getAnnotation(Component.class);
                if (component != null) {
                    doInitClass(aClass, component.value());
                }
            }
        }
    }

    /**
     * 反射初始化目标类
     *
     * @param aClass
     * @param value
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private static void doInitClass(Class<?> aClass, String value) throws IllegalAccessException, InstantiationException {
        Object o = aClass.newInstance();
        // 将对象放入map中
        // 1. 对象的注解中有值则多存一份值名为key的对象
        if (! value.equals("")) {
            map.put(value, o);
        }
        // 全类名为key
        String className = aClass.getName();
        map.put(getCamelClassName(className), o);

        // 存一份只有类名为key
        map.put(getCamelClassName(className.substring(className.lastIndexOf(STRING_FILE_SEPARATOR) + 1)), o);

        // 查看对象类是否有实现的接口，若存在，则将对象存一份实现的接口
        Class<?>[] interfaces = aClass.getInterfaces();
        if (interfaces != null) {
            for (Class<?> classInterface : interfaces) {
                // 接口全类名为key
                String classInterfaceName = classInterface.getName();
                map.put(getCamelClassName(classInterfaceName), o);
                // 接口类名为key
                map.put(getCamelClassName(classInterfaceName.substring(classInterfaceName.lastIndexOf(STRING_FILE_SEPARATOR) + 1)), o);
            }
        }

        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field : declaredFields) {
            if (field.getAnnotation(Autowired.class) != null) {
                autoWiredClassSet.add(o);
                break;
            }
        }
    }

    /**
     * 获取小驼峰式类名
     *
     * @param className
     * @return
     */
    private static String getCamelClassName(String className) {
        className = className.substring(0, 1).toLowerCase() + className.substring(1);
        return className;
    }


    // 任务二：对外提供获取实例对象的接口（根据id获取）
    public static Object getBean(String id) {
        return map.get(id);
    }


}
